using System.Collections.Generic;
using System;
using UnityEngine;
/// <summary>
/// State Generic class,
/// The Base class of all states and FSMs
/// </summary>
/// <typeparam name="StateType">may be different enum, string etc</typeparam>
public abstract class State<StateType> {
	public StateType StateName { get; private set; }

	public State<StateType> CurState { get; set; } = null;

	//The conditions of transite to the same level state or fsm
	public Dictionary<StateType, Func<bool>> TransitionsTo { get; private set; } = new Dictionary<StateType, Func<bool>>();

	// Condition of Transite from other state to this,
	// usually used by a superFsm to decide which state to start
	public Func<bool> ConditionToSelf { get; private set; }

	protected State<StateType> SuperFSM { get; private set; }

	protected StateFactory<StateType> Factory { get; private set; }

	public State(StateType stateName, State<StateType> superFSM, StateFactory<StateType> factory) {
		StateName = stateName;
		TransitionsTo = new Dictionary<StateType, Func<bool>>();
		SuperFSM = superFSM;
		Factory = factory;
	}

	public abstract void Enter();

	public virtual void Update() {
		CheckTransition();
	}

	public abstract void Exit();

	public virtual void CheckTransition() {
		//Debug.Log($"{TransitionsTo.Count}");
		foreach (var item in TransitionsTo) {
			//if (CurState is PlayerSlide) {
			//	Debug.Log("Slide");
			//}
			if (CheckAllFuncs(item.Value)) {
				ChangeStateTo(item.Key);
			}
		}
	}

	public virtual void ChangeStateTo(StateType state) {
		Exit();
		var nextState = Factory.GetState(state);
		if (SuperFSM != null)
			SuperFSM.CurState = nextState;
		nextState.Enter();
	}

	public void AddConditionToSelf(Func<bool> condition) {
		ConditionToSelf += condition;
	}

	public void AddNewTransition(StateType state, Func<bool> condition) {

#if UNITY_EDITOR
		if (TransitionsTo.ContainsKey(state)) {
			Debug.Log($"The New State:{state} already In Transition List.");
			return;
		}
#endif
		TransitionsTo.Add(state, condition);
	}

	public void AddNewTrasitionCondition(StateType state, Func<bool> condition) {

#if UNITY_EDITOR
		if (!TransitionsTo.ContainsKey(state)) {
			Debug.Log($"The State:{state} not In Transition List.");
			return;
		}
#endif
		TransitionsTo[state] += condition;
	}

	public void MinusTransitionCon(StateType state, Func<bool> condition) {
#if UNITY_EDITOR
		if (!TransitionsTo.ContainsKey(state)) {
			Debug.Log($"The State:{state} not In Transition List.");
			return;
		}
#endif
		TransitionsTo[state] = Delegate.Remove(TransitionsTo[state], condition) as Func<bool>;

	}

	protected bool IsZero(float value) => Mathf.Abs(value) < 0.01;

	protected bool AreSignsEqual<T1, T2>(T1 num1, T2 num2)
		where T1 : struct, IComparable, IFormattable, IConvertible
		where T2 : struct, IComparable, IFormattable, IConvertible {
		int sign1 = Math.Sign(Convert.ToDouble(num1));
		int sign2 = Math.Sign(Convert.ToDouble(num2));

		return (sign1 == sign2);
	}

	// Check if All The transition condition is ok
	private bool CheckAllFuncs(Func<bool> func) {
		var funcs = func.GetInvocationList();
		foreach (var del in funcs) {
			var function = (Func<bool>)del;
			if (!function()) {
				return false;
			}
		}
		return true;
	}

}
